]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
util: Add AppSettingsMonitor
authorFlorian Müllner <fmuellner@gnome.org>
Thu, 23 Feb 2017 21:50:04 +0000 (22:50 +0100)
committerFlorian Müllner <fmuellner@gnome.org>
Mon, 27 Feb 2017 19:02:16 +0000 (20:02 +0100)
When integrating with optional components like Clocks, it is not safe
to access their GSettings right after the application became visible
to the AppSystem:
Installation is usually not atomic, so the .desktop file may appear
before the settings schema, in which case Gio will abort due to an
"invalid" schema ID.
To address this, add a small helper class that wraps the settings
access in a safe way.

https://bugzilla.gnome.org/show_bug.cgi?id=766410

js/misc/util.js

index 7c6d1112499d4e52f8d487701dea804985eb9c7b..0221e15af583bc99b9fd946b215abca4f8b64168 100644 (file)
@@ -4,6 +4,8 @@ const Clutter = imports.gi.Clutter;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Signals = imports.signals;
 const Shell = imports.gi.Shell;
 const St = imports.gi.St;
 
@@ -398,3 +400,94 @@ function ensureActorVisibleInScrollView(scrollView, actor) {
                        time: SCROLL_TIME,
                        transition: 'easeOutQuad' });
 }
+
+const AppSettingsMonitor = new Lang.Class({
+    Name: 'AppSettingsMonitor',
+
+    _init: function(appId, schemaId) {
+        this._appId = appId;
+        this._schemaId = schemaId;
+
+        this._app = null;
+        this._settings = null;
+        this._handlers = [];
+
+        this._schemaSource = Gio.SettingsSchemaSource.get_default();
+
+        this._appSystem = Shell.AppSystem.get_default();
+        this._appSystem.connect('installed-changed',
+                                Lang.bind(this, this._onInstalledChanged));
+        this._onInstalledChanged();
+    },
+
+    get available() {
+        return this._app != null && this._settings != null;
+    },
+
+    activateApp: function() {
+        if (this._app)
+            this._app.activate();
+    },
+
+    watchSetting: function(key, callback) {
+        let handler = { id: 0, key: key, callback: callback };
+        this._handlers.push(handler);
+
+        this._connectHandler(handler);
+    },
+
+    _connectHandler: function(handler) {
+        if (!this._settings || handler.id > 0)
+            return;
+
+        handler.id = this._settings.connect('changed::' + handler.key,
+                                            handler.callback);
+        handler.callback(this._settings, handler.key);
+    },
+
+    _disconnectHandler: function(handler) {
+        if (this._settings && handler.id > 0)
+            this._settings.disconnect(handler.id);
+        handler.id = 0;
+    },
+
+    _onInstalledChanged: function() {
+        let hadApp = (this._app != null);
+        this._app = this._appSystem.lookup_app(this._appId);
+        let haveApp = (this._app != null);
+
+        if (hadApp == haveApp)
+            return;
+
+        if (haveApp)
+            this._checkSettings();
+        else
+            this._setSettings(null);
+    },
+
+    _setSettings(settings) {
+        this._handlers.forEach((handler) => { this._disconnectHandler(handler); });
+
+        let hadSettings = (this._settings != null);
+        this._settings = settings;
+        let haveSettings = (this._settings != null);
+
+        this._handlers.forEach((handler) => { this._connectHandler(handler); });
+
+        if (hadSettings != haveSettings)
+            this.emit('available-changed');
+    },
+
+    _checkSettings: function() {
+        let schema = this._schemaSource.lookup(this._schemaId, true);
+        if (schema) {
+            this._setSettings(new Gio.Settings({ settings_schema: schema }));
+        } else if (this._app) {
+            Mainloop.timeout_add_seconds(1, () => {
+                this._checkSettings();
+                return GLib.SOURCE_REMOVE;
+            });
+        }
+    }
+});
+Signals.addSignalMethods(AppSettingsMonitor.prototype);